(0.4.0) Add radiation as a top-level EarthSystemModel component#200
(0.4.0) Add radiation as a top-level EarthSystemModel component#200
Conversation
Wholesale refactor: radiation moves out of ComponentInterfaces and becomes its own model component, mirroring the land PR (#149) shape. - New src/Radiations/ module with PrescribedRadiation, SurfaceRadiationProperties, InterfaceRadiationFlux, the radiation kernels, and the moved albedo modules. - EarthSystemModel struct: type-param order {R, A, L, I, O, F, C, Arch}; radiation placed above atmosphere ("from the sun"). Default `radiation = nothing` means radiatively decoupled (no upwelling LW, no SW/LW absorption). - StateExchanger gains a radiation slot; component_interfaces takes radiation as a kwarg used only to construct the radiation exchanger. - Kernel split: _assemble_net_ocean_fluxes! and _assemble_net_sea_ice_fluxes! now do turbulent contributions only. New apply_air_sea_radiative_fluxes! and apply_air_sea_ice_radiative_fluxes! kernels (in Radiations/) add radiative contributions on top, dispatch on `coupled_model.radiation` type, and write to per-surface InterfaceRadiationFlux diagnostics. - SkinTemperature consumes radiation via the new `air_sea_interface_radiation_state` (and sea-ice variant) getter, which returns zeros when radiation is off — so SkinTemperature degrades cleanly to a turbulent-only flux balance. - PrescribedAtmosphere drops downwelling_radiation / TwoBandDownwellingRadiation; atmosphere exchanger state shrinks to (u, v, T, p, q, Jᶜ). - New JRA55PrescribedRadiation, ECCOPrescribedRadiation, OSPapaPrescribedRadiation data-wrangling constructors. - Reactant ext updated for the new type-param order. Tests, examples, and docs migrations to follow. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Replace Radiation(arch) with JRA55PrescribedRadiation(arch; backend) in test_ocean_only_model.jl, test_ocean_sea_ice_model.jl, test_sea_ice_ocean_heat_fluxes.jl, test_checkpointer.jl, test_reactant.jl. - Replace 'no radiation' Radiation(emissivity=0, albedo=1) and Radiation() defaults with radiation=nothing in test_surface_fluxes.jl, test_diagnostics_2.jl, test_speedy_coupling.jl. - Update test_ecco_atmosphere.jl and test_ospapa.jl to also build ECCOPrescribedRadiation / OSPapaPrescribedRadiation and assert against radiation.downwelling_shortwave / longwave. - New test_radiations.jl covers PrescribedRadiation construction, time_step!, pairing with OceanOnlyModel (verifies interface_fluxes allocation), and JRA55PrescribedRadiation loading. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- examples/{idealized_single_column,global_climate,near_global_ocean,
one_degree,single_column_os_papa,meridional_heat_transport_ecco,
veros_ocean_forced,generate_surface_fluxes}.jl all use the new
PrescribedRadiation / JRA55PrescribedRadiation / OSPapaPrescribedRadiation /
ECCOPrescribedRadiation API.
- experiments/{arctic,one_degree,coupled,flux_climatology}.jl migrated.
- experiments/coupled passes a LatitudeDependentAlbedo through the new
SurfaceRadiationProperties bundle.
- docs/src/earth_system_model.md doctest output reflects the new
'radiation:' line in the EarthSystemModel show.
- Fix FreezingLimitedEarthSystemModel and Single*FreezingLimited type
aliases to use the new {R, A, L, I, O, F, C, Arch} type-parameter order.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
- SurfaceRadiationProperties gains a kwarg constructor with a default emissivity (0.97), letting users write `SurfaceRadiationProperties(albedo = 0.1)` to override only the albedo. - New top-level `default_stefan_boltzmann_constant = 5.670374419e-8` constant in Radiations; all PrescribedRadiation / data-wrangling constructors now default to it. - Reorder positional arguments to match struct (top→bottom) convention: - `EarthSystemModel(radiation, atmosphere, land, sea_ice, ocean; ...)` - `OceanSeaIceModel(sea_ice, ocean; ...)` All call sites in src, tests, examples, experiments, and docs flipped. - Fix indent in ECCOPrescribedRadiation docstring. - Apply review-suggested formatting to air_sea_(ice_)interface_radiation_state. - Update docstring in earth_system_model.jl to reflect the new positional-arg convention. - one_degree_simulation.jl now demonstrates a custom ocean albedo via `SurfaceRadiationProperties(albedo = LatitudeDependentAlbedo())`. - Fix Speedy comment in global_climate_simulation.jl: NumericalEarth still computes turbulent fluxes; only the radiation path is unwired against Speedy upwelling-LW. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Addressed the comments in commit 83a2151:
Skipped (per your scope-creep notes): The two |
The new `EarthSystemModel(; radiation, atmosphere, land, sea_ice, ocean, kw...)` form is equivalent to the positional one but skips the awkward `nothing` padding for absent components: EarthSystemModel(; atmosphere, ocean) # ocean + atmosphere EarthSystemModel(; atmosphere, sea_ice, ocean, radiation) # full coupled All previously-padded call sites in test_speedy_coupling.jl, examples/global_climate_simulation.jl, and docs/src/developers/slab_ocean.jl switch to the kwarg form for readability. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
test_ocean_sea_ice_model.jl line 82 still had the old OceanSeaIceModel(ocean_with_land, sea_ice_with_land; ...) order. The earlier sed only matched literal 'ocean,' so it skipped this variant. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The test/Manifest.toml was committed by mistake in b9260d8. Each Julia version generates its own resolved manifest from test/Project.toml, so committing one causes 'manifest differs' warnings (and ultimately resolution failures) on CI runs that use a different Julia version than the one the manifest was generated against. Add test/Manifest.toml to .gitignore alongside the existing root and docs entries. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GPU CI hit a NetCDF: HDF error opening JRA55/RYF.rlds.1990_1991.nc — a corrupted cached download. The existing init guard only constructs a JRA55PrescribedAtmosphere, which never touches the radiation files (rlds/rsds). Now that those files are loaded at test time via JRA55PrescribedRadiation, a bad cached file isn't caught until much later. Construct a JRA55PrescribedRadiation in the same try/catch so the NumericalEarthArtifacts fallback path triggers for radiation downloads too, mirroring the atmosphere variables. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
I expect that adding something like |
| surface_layer_height = surface_layer_height(coupled_model.atmosphere), | ||
| gravitational_acceleration = coupled_model.interfaces.properties.gravitational_acceleration) | ||
|
|
||
| # Radiation state for the interface solve (used by SkinTemperature). |
There was a problem hiding this comment.
General question: Is the same SkinTemperature used over the ocean and land? And are we missing something like src/EarthSystemModels/InterfaceComputations/atmosphere_land.jl following #149?
There was a problem hiding this comment.
Currently SkinTemperature can be used by sea ice or ocean... we don't have a prognostic land yet for which this could be used, but that would definitely be the plan.
There was a problem hiding this comment.
Re atmosphere_land.jl this needs to be added in order to enable atmosphere simulations with prescribed or prognostic land... something that we cannot do yet, because the land component is very minimal placeholder right now. So yeah, more work needed.
| end | ||
|
|
||
| ZeroFluxes() = ZeroFluxes(ntuple(_ -> ZeroField(), 14)...) | ||
| ZeroFluxes() = ZeroFluxes(ntuple(_ -> ZeroField(), 11)...) |
There was a problem hiding this comment.
Is something like
Nfields = fieldcount(ZeroFluxes)
ZeroFluxes() = ZeroFluxes(ntuple(_ -> ZeroField(), Nfields)...)bad form?
There was a problem hiding this comment.
That won't work as-is because Nfields is global, which would cause a type inference failure. But this might work:
ZeroFluxes() = ZeroFluxes(ntuple(_ -> ZeroField(), fieldcount(ZeroFluxes))...)or adding const in front of Nfields
| @inline net_absorbed_interface_radiation(ℐꜜˢʷ, ℐꜜˡʷ, α, ϵ) = - (1 - α) * ℐꜜˢʷ - ϵ * ℐꜜˡʷ | ||
| @inline emitted_longwave_radiation(T, σ, ϵ) = σ * ϵ * T^4 |
There was a problem hiding this comment.
Are these ever called? It seems like the same code is repeated throughout:
- src/Radiations/apply_air_sea_radiative_fluxes.jl
- src/Radiations/apply_air_sea_ice_radiative_fluxes.jl
- src/Radiations/radiation_kernels.jl
There was a problem hiding this comment.
hmm that is concerning. let me look at that
ewquon
left a comment
There was a problem hiding this comment.
This is a nice bit of surgery @glwagner. The implemented code reflect the described development, as far as I can tell. My main concern is the dead/repeated code in radiation_kernels.jl 6fd48b4#r3174870639
…es.jl Co-authored-by: Eliot Quon <eliot@aeolus.earth>
Co-authored-by: Eliot Quon <eliot@aeolus.earth>
Co-authored-by: Eliot Quon <eliot@aeolus.earth>
Yes! And we can either convert one of the existing examples or make a new example that is forced by ERA5 rather than JRA55. I think this is important. |
Summary
Closes #30. Wholesale refactor that promotes radiation from a
ComponentInterfacesfield into its own top-levelEarthSystemModelcomponent, mirroring the structure of #149 (land).src/Radiations/module withPrescribedRadiation,SurfaceRadiationProperties,InterfaceRadiationFlux, the movedLatitudeDependentAlbedo/TabulatedAlbedomodules, and the radiation kernels.EarthSystemModelstruct: type-param order{R, A, L, I, O, F, C, Arch};radiationfield placed aboveatmosphere("from the sun"). Defaultradiation = nothing⇒ radiatively decoupled (no upwelling LW, no SW/LW absorption).StateExchangergains aradiationslot.ComponentInterfacesconstructor takesradiationas a kwarg used only to construct the radiation exchanger._assemble_net_ocean_fluxes!and_assemble_net_sea_ice_fluxes!now do only turbulent + sea-ice contributions. Newapply_air_sea_radiative_fluxes!andapply_air_sea_ice_radiative_fluxes!(inRadiations/) add radiative contributions on top, dispatch wholesale oncoupled_model.radiationtype, and write to per-surfaceInterfaceRadiationFluxdiagnostics.SkinTemperatureconsumes radiation via the newair_sea_interface_radiation_state(rk, ex_state, i, j, k, grid, time)getter (and sea-ice variant). When radiation is off the getter returns zeros, soSkinTemperaturedegrades cleanly to a turbulent-only flux balance.PrescribedAtmospherelosesdownwelling_radiation;TwoBandDownwellingRadiationis removed entirely (committing to two-band).JRA55PrescribedRadiation,ECCOPrescribedRadiation,OSPapaPrescribedRadiation.Migration notes (breaking changes for v0.3)
Radiation()is gone. To enable radiation, build aPrescribedRadiation(or one of the data-wrangling helpers) and pass it viaradiation = ...toOceanOnlyModel/OceanSeaIceModel/EarthSystemModel.radiation = nothing(the new default) means no radiation at all — no incoming SW/LW absorption and no ϵσT⁴ surface emission. Existing JRA55-driven simulations need to constructJRA55PrescribedRadiation(arch; backend, ...)explicitly.atmosphere.downwelling_radiationno longer exists. Read fromradiation.downwelling_shortwave/radiation.downwelling_longwaveinstead.SurfaceRadiationProperties(albedo, emissivity). Pass them to constructors asocean_surface = SurfaceRadiationProperties(0.05, 0.97)etc.Test plan
test_radiations.jlcoversPrescribedRadiationconstruction (FTS form + grid-only form),time_step!, surface-property filtering, pairing withOceanOnlyModel(verifiesinterface_fluxesallocation), andJRA55PrescribedRadiationloading. (Locally: 24/24 passing.)test_ocean_only_model.jl— 5/5 passing locally withJRA55PrescribedRadiation.test_surface_fluxes.jl— 24/24 passing locally; "no-radiation" cases useradiation=nothingdirectly.test_diagnostics_2.jl— 44/44 passing locally withradiation=nothing.test_jra55,test_ocean_sea_ice_model,test_sea_ice_ocean_heat_fluxes,test_checkpointer,test_ecco_atmosphere,test_ospapa,test_reactant,test_speedy_coupling.🤖 Generated with Claude Code